package org.create.builder;

import org.create.bean.FieldInfo;
import org.create.bean.TableInfo;
import org.create.utils.IoTool;
import org.create.utils.LogTool;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.create.bean.Constants.*;


public class BuildMapperXml {
    private static BufferedWriter bw;
    private static final String baseResultMapId = "base_result_map";
    private static final String baseColumnListId = "base_column_list";
    private static final String baseQueryCondition = "base_query_condition";
    private static final String baseQueryConditionExtendId = "base_query_condition_extend";
    private static final String queryConditionId = "query_condition";
    private static final String selectListId = "selectList";
    private static final String selectCountId = "selectCount";
    private static final String insert = "insert";
    private static final String insertOrUpdate = "insertOrUpdate";
    private static final String insertBatch = "insertBatch";
    private static final String insertOrUpdateBatch = "insertOrUpdateBatch";

    private static void buildDTD() throws IOException {
        writer("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"");
        writer("        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">");
    }

    private static void buildMapper(TableInfo tableInfo) throws IOException {
        writer("<mapper namespace=\""+ PACKAGE_BASE + "." + PACKAGE_MAPPERS_XML + "." + tableInfo.getBeanName() + "Mapper" + "\">");
        buildResultMap(tableInfo);
        buildGeneralResult(tableInfo);
        buildBaseConditionFiled(tableInfo);
        buildQueryCondition(tableInfo);
        buildBaseCondition();
        buildSelectList(tableInfo);
        buildSelectCount(tableInfo);
        buildInsert(tableInfo);
        buildInsertOrUpdate(tableInfo);
        buildInsertBatch(tableInfo);
        buildInsertOrUpdateBatch(tableInfo);
        buildUniqueQuery(tableInfo);
        writer("</mapper>");
    }

    private static void buildResultMap(TableInfo tableInfo) throws IOException {
        writer("    <!--实体映射-->");
        writer("    <resultMap id=\"" + baseResultMapId + "\" type=\"" + PACKAGE_BASE + "." + PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        boolean firstLoop = true;
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            writer("        <!--" + fieldInfo.getComment() + "-->");
            if (firstLoop) {
                writer("        <id column=\"" + fieldInfo.getFieldName() + "\" property=\"" + fieldInfo.getPropertyName() + "\"/>");
                firstLoop = false;
            } else {
                writer("        <result column=\"" + fieldInfo.getFieldName() + "\" property=\"" + fieldInfo.getPropertyName() + "\"/>");
            }
        }
        writer("    </resultMap>", 2);
    }

    private static void buildGeneralResult(TableInfo tableInfo) throws IOException {
        writer("    <!--通用查询结果列-->");
        writer("    <sql id=\"" + baseColumnListId + "\">", 0);
        int loop = 1;
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            writer(fieldInfo.getFieldName(), 0);
            if (loop < tableInfo.getFieldList().size()) {
                writer(", ", 0);
                loop++;
            }
        }
        writer("</sql>", 2);
    }

    private static void buildBaseConditionFiled(TableInfo tableInfo) throws IOException {
        writer("    <!--基础查询条件-->");
        writer("    <sql id=\"" + baseQueryCondition + "\">");
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            String beanName = fieldInfo.getPropertyName();
            String sql_name = fieldInfo.getFieldName();
            if (fieldInfo.getJavaType().equals("String")) {
                writer("        <if test=\"query." + beanName + " != null and query." + beanName + " != ''\">and " + sql_name + " = #{query." + beanName + "}</if>");
            } else if (fieldInfo.getJavaType().equals("Date")) {
                writer("        <if test=\"query." + beanName + " != null\"><![CDATA[ and " + sql_name + " = str_to_date(#{query." + beanName + "}, '%Y-%m-%d')]]></if>");
            } else {
                writer("        <if test=\"query." + beanName + " != null\">and " + sql_name + " = #{query." + beanName + "}</if>");
            }
        }
        writer("    </sql>", 2);
    }

    private static void buildQueryCondition(TableInfo tableInfo) throws IOException {
        writer("    <!--扩展查询条件列-->");
        writer("    <sql id=\"" + baseQueryConditionExtendId + "\">");
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            String beanName = fieldInfo.getPropertyName() + QUERY_FUZZY;
            String sql_name = fieldInfo.getFieldName();
            if (fieldInfo.getJavaType().equals("String")) {
                writer("            <if test=\"query." + beanName + " != null and query." + beanName + " != ''\">");
                writer("                and " + sql_name + " like concat('%', #{query." + beanName + "}, '%')");
                writer("            </if>");
            } else if (fieldInfo.getJavaType().equals("Date")) {
                beanName = fieldInfo.getPropertyName();
                writer("            <if test=\"query." + beanName + QUERY_TIME_START + " != null\">");
                writer("                <![CDATA[ and " + sql_name + " >= str_to_date(#{query." + beanName + QUERY_TIME_START + "}, '%Y-%m-%d')]]>");
                writer("            </if>");
                writer("            <if test=\"query." + beanName + QUERY_TIME_END + " != null\">");
                writer("                <![CDATA[ and " + sql_name + " < str_to_date(#{query." + beanName + QUERY_TIME_END + "}, '%Y-%m-%d') + interval 1 day]]>");
                writer("            </if>");
            }
        }
        writer("    </sql>", 2);
    }

    private static void buildBaseCondition() throws IOException {
        writer("    <!--扩展查询条件-->");
        writer("    <sql id=\"" + queryConditionId + "\">");
        writer("        <where>");
        writer("            <include refid=\"" + baseQueryCondition + "\"/>");
        writer("            <include refid=\"" + baseQueryConditionExtendId + "\"/>");
        writer("        </where>");
        writer("    </sql>", 2);
    }

    private static void buildSelectList(TableInfo tableInfo) throws IOException {
        writer("    <!--查询集合-->");
        writer("    <select id=\"" + selectListId + "\" resultMap=\"" + baseResultMapId + "\">");
        writer("        SELECT<include refid=\"" + baseColumnListId + "\"/>");
        writer("            FROM " + tableInfo.getTableName());
        writer("        <include refid=\"" + queryConditionId + "\"/>");
        writer("        <if test=\"query.order != null\">order by ${query.order}</if>");
        writer("        <if test=\"query.simplePage != null\">limit #{query.simplePage.start}, #{query.simplePage.end}</if>");
        writer("    </select>", 2);
    }

    private static void buildSelectCount(TableInfo tableInfo) throws IOException {
        writer("    <!--查询数量-->");
        writer("    <select id=\"" + selectCountId + "\" resultType=\"java.lang.Integer\">");
        writer("        SELECT count(1) FROM " + tableInfo.getTableName());
        writer("        <include refid=\"" + queryConditionId + "\"/>");
        writer("    </select>", 2);
    }

    private static void buildInsert(TableInfo tableInfo) throws IOException {
        writer("    <!--插入-->");
        List<FieldInfo> fieldInfoList = tableInfo.getFieldList();
        writer("    <insert id=\"" + insert + "\" parameterType=\"" + PACKAGE_BASE + "." + PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        writer("        INSERT INTO " + tableInfo.getTableName());
        writer("        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        for (FieldInfo fieldInfo : fieldInfoList) {
            writer("            <if test=\"bean." + fieldInfo.getPropertyName() + " != null\">" + fieldInfo.getFieldName() + ",</if>");
        }
        writer("        </trim>");
        writer("        <trim prefix=\"VALUES (\" suffix=\")\" suffixOverrides=\",\">");
        for (FieldInfo fieldInfo : fieldInfoList) {
            writer("            <if test=\"bean." + fieldInfo.getPropertyName() + " != null\">#{bean." + fieldInfo.getPropertyName() + "},</if>");
        }
        writer("        </trim>");
        writer("    </insert>", 2);
    }

    private static void buildInsertOrUpdate(TableInfo tableInfo) throws IOException {
        writer("    <!--插入或更新-->");
        List<FieldInfo> fieldInfoList = tableInfo.getFieldList();
        writer("    <insert id=\"" + insertOrUpdate + "\" parameterType=\"" + PACKAGE_BASE + "." + PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        writer("        INSERT INTO " + tableInfo.getTableName());
        writer("        <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
        for (FieldInfo fieldInfo : fieldInfoList) {
            writer("            <if test=\"bean." + fieldInfo.getPropertyName() + " != null\">" + fieldInfo.getFieldName() + ",</if>");
        }
        writer("        </trim>");
        writer("        <trim prefix=\"VALUES (\" suffix=\")\" suffixOverrides=\",\">");
        for (FieldInfo fieldInfo : fieldInfoList) {
            writer("            <if test=\"bean." + fieldInfo.getPropertyName() + " != null\">#{bean." + fieldInfo.getPropertyName() + "},</if>");
        }
        writer("        </trim>");
        writer("        on DUPLICATE key update");
        writer("        <trim prefix=\"\" suffix=\"\" suffixOverrides=\",\">");
        List<FieldInfo> keyFiled = new ArrayList<>();
        for (Map.Entry<String, List<FieldInfo>> entry : (tableInfo.getKeyIndexMap().entrySet())) {
            keyFiled.addAll(entry.getValue());
        }
        List<FieldInfo> noKeyFiled = fieldInfoList.stream().filter(e -> !keyFiled.contains(e)).collect(Collectors.toList());
        for (FieldInfo fieldInfo : noKeyFiled) {
            writer("            <if test=\"bean." + fieldInfo.getPropertyName() + " != null\">" + fieldInfo.getFieldName() + " = VALUES(" + fieldInfo.getFieldName() + "),</if>");
        }
        writer("        </trim>");
        writer("    </insert>", 2);
    }

    private static void buildInsertBatch(TableInfo tableInfo) throws IOException {
        writer("    <!--批量插入-->");
        writer("    <insert id=\"" + insertBatch + "\" parameterType=\"" + PACKAGE_BASE + "." + PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        writer("        INSERT INTO " + tableInfo.getTableName() + "(", 0);
        int loop = 1;
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            writer(fieldInfo.getFieldName(), 0);
            if (loop < tableInfo.getFieldList().size()) {
                loop++;
                writer(", ", 0);
            }
        }
        loop = 1;
        writer(") VALUES");
        writer("        <foreach collection=\"list\" item=\"item\" separator=\",\">");
        writer("            (", 0);
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            writer("#{item." + fieldInfo.getPropertyName() + "}", 0);
            if (loop < tableInfo.getFieldList().size()) {
                loop++;
                writer(", ", 0);
            }
        }
        writer(")");
        writer("        </foreach>");
        writer("    </insert>", 2);
    }

    private static void buildInsertOrUpdateBatch(TableInfo tableInfo) throws IOException {
        writer("    <!--批量插入或更新-->");
        writer("    <insert id=\"" + insertOrUpdateBatch + "\" parameterType=\"" + PACKAGE_BASE + "." + PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
        writer("        INSERT INTO " + tableInfo.getTableName() + "(", 0);
        int loop = 1;
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            writer(fieldInfo.getFieldName(), 0);
            if (loop < tableInfo.getFieldList().size()) {
                loop++;
                writer(", ", 0);
            }
        }
        loop = 1;
        writer(") VALUES");
        writer("        <foreach collection=\"list\" item=\"item\" separator=\",\">");
        writer("            (", 0);
        for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
            writer("#{item." + fieldInfo.getPropertyName() + "}", 0);
            if (loop < tableInfo.getFieldList().size()) {
                loop++;
                writer(", ", 0);
            }
        }
        writer(")");
        writer("        on DUPLICATE key update");
        writer("        <trim prefix=\"\" suffix=\"\" suffixOverrides=\",\">");
        List<FieldInfo> keyFiled = new ArrayList<>();
        for (Map.Entry<String, List<FieldInfo>> entry : (tableInfo.getKeyIndexMap().entrySet())) {
            keyFiled.addAll(entry.getValue());
        }
        List<FieldInfo> fieldInfoList = tableInfo.getFieldList();
        List<FieldInfo> noKeyFiled = fieldInfoList.stream().filter(e -> !keyFiled.contains(e)).collect(Collectors.toList());
        for (FieldInfo fieldInfo : noKeyFiled) {
            writer("            <if test=\"item." + fieldInfo.getPropertyName() + " != null\">" + fieldInfo.getFieldName() + " = VALUES(" + fieldInfo.getFieldName() + "),</if>");
        }
        writer("        </trim>");
        writer("        </foreach>");
        writer("    </insert>", 2);
    }

    private static void buildUniqueQuery(TableInfo tableInfo) throws IOException {
        List<FieldInfo> keyFiled = new ArrayList<>();
        for (Map.Entry<String, List<FieldInfo>> entry : (tableInfo.getKeyIndexMap().entrySet())) {
            keyFiled.addAll(entry.getValue());
        }
        List<FieldInfo> fieldInfoList2 = tableInfo.getFieldList();
        List<FieldInfo> noKeyFiled = fieldInfoList2.stream().filter(e -> !keyFiled.contains(e)).collect(Collectors.toList());
        for (Map.Entry<String, List<FieldInfo>> entry : tableInfo.getKeyIndexMap().entrySet()) {
            List<FieldInfo> fieldInfoList = entry.getValue();
            StringBuilder sb = new StringBuilder();
            StringBuilder params = new StringBuilder();
            int firstLoop = 1;
            for (FieldInfo fieldInfo : fieldInfoList) {
                sb.append(fieldInfo.getPropertyName().substring(0, 1).toUpperCase()).append(fieldInfo.getPropertyName().substring(1));
                params.append(fieldInfo.getFieldName()).append("=#{").append(fieldInfo.getPropertyName()).append("}");
                if (firstLoop < fieldInfoList.size()) {
                    sb.append("And");
                    params.append(" and ");
                    firstLoop++;
                }
            }
            writer("    <!--根据" + sb + "查询-->");
            writer("    <select id=\"selectBy" + sb + "\" resultMap=\"" + baseResultMapId + "\">");
            writer("        SELECT <include refid=\"" + baseColumnListId + "\"/> FROM " + tableInfo.getTableName() + " where " + params);
            writer("    </select>", 2);
            writer("    <!--根据" + sb + "删除-->");
            writer("    <delete id=\"deleteBy" + sb + "\">");
            writer("        DELETE FROM " + tableInfo.getTableName() + " where " + params);
            writer("    </delete>", 2);
            writer("    <!--根据" + sb + "更新-->");
            writer("    <update id=\"updateBy" + sb + "\" parameterType=\"" + PACKAGE_BASE + "." + PACKAGE_PO + "." + tableInfo.getBeanName() + "\">");
            writer("        UPDATE " + tableInfo.getTableName());
            writer("        <set>");
            for (FieldInfo fieldInfo : noKeyFiled) {
                writer("            <if test=\"bean." + fieldInfo.getPropertyName() + " != null\">" + fieldInfo.getFieldName() + "=#{bean." + fieldInfo.getPropertyName() + "},</if>");
            }
            writer("        </set>");
            writer("        WHERE " + params);
            writer("    </update>", 2);
        }
    }

    private static void writer(String str) throws IOException {
        writer(str, 1);
    }

    private static void writer(String str, int num) throws IOException {
        bw.write(str);
        if (num > 0) {
            appendLine(num);
        }
    }

    private static void appendLine(int loop) throws IOException {
        for (int i = 0; i < loop; i++) {
            bw.newLine();
        }
    }

    public static void run(List<TableInfo> tableInfoList) {
        for (TableInfo tableInfo : tableInfoList) {
            File file = new File(PATH_MAPPERS_XML + tableInfo.getBeanName() + "Mapper" + XML_SUFFIX);
            File dir = file.getParentFile();
            boolean d = dir.mkdirs();
            LogTool.printLog(d, "新建目录", dir);
            try {
                boolean r = file.createNewFile();
                LogTool.printLog(r, "新建文件", file);
                bw = IoTool.createBw(file);
                buildDTD();
                buildMapper(tableInfo);
            } catch (Exception e) {
                LogTool.printError(e);
            } finally {
                IoTool.close();
            }
        }
    }
}
